package dataMaintenance;

import lib.common.Util;
import lib.database.Complex;
import lib.database.LibDB;
import lib.database.LibSearch;
import lib.database.ProgramDataDB;
import lib.huvud.Div;
import lib.huvud.ProgramConf;
import lib.huvud.SortedListModel;

/** Find out possible errors in the databases in the list.
 * <br>
 * Copyright (C) 2015 I.Puigdomenech.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 * 
 * @author Ignasi Puigdomenech */
public class Statistics extends javax.swing.JFrame {
  private boolean finished = false;
  private final ProgramDataDB pd;
  private final ProgramConf pc;
  //private String INIfileName;
  private int na, nx;
  private java.io.PrintWriter outputFile;

  private final javax.swing.DefaultListModel modelCompsFnd = new javax.swing.DefaultListModel();
  private final SortedListModel sortedModelCompsFnd = new SortedListModel(modelCompsFnd);
  private final javax.swing.DefaultListModel modelNameWoutNumber = new javax.swing.DefaultListModel();
  private final SortedListModel sortedModelNameWoutNumber = new SortedListModel(modelNameWoutNumber);
  private final javax.swing.DefaultListModel modelNumberWoutName = new javax.swing.DefaultListModel();
  private final SortedListModel sortedModelNumberWoutName = new SortedListModel(modelNumberWoutName);
  private final javax.swing.DefaultListModel modelHConflict = new javax.swing.DefaultListModel();
  private final SortedListModel sortedModelHConflict = new SortedListModel(modelHConflict);
  private final javax.swing.DefaultListModel modelChargeError = new javax.swing.DefaultListModel();
  private final SortedListModel sortedModelChargeError = new SortedListModel(modelChargeError);

  /** keep a list of reactants found in the reaction-databases, and for each reactant
   * in how many reactions it participates. The sorting is done in sortedModelCompsFnd. */
  private final java.util.HashMap<String, Integer> itemsCompsFnd = new java.util.HashMap<String, Integer>();

  private final javax.swing.border.Border defBorder;
  private final javax.swing.border.Border highlightedBorder =
             javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED,
                new java.awt.Color(102,102,102),
                new java.awt.Color(255,255,255),
                new java.awt.Color(102,102,102),
                new java.awt.Color(0,0,0));

  private java.awt.Dimension windowSize = new java.awt.Dimension(400,280);

  /** New-line character(s) to substitute "\n" */
  private static final String nl = System.getProperty("line.separator");
  private static final String SLASH = java.io.File.separator;

//<editor-fold defaultstate="collapsed" desc="Constructor">
  /** Creates new form Statistics
   * @param pc0
   * @param pd0  */
  public Statistics(
          ProgramConf pc0,
          ProgramDataDB pd0) {
    initComponents();
    pc = pc0;
    pd = pd0;
    defBorder = jScrollPaneComps.getBorder();  // get the default list border
    setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
    //--- Close window on ESC key
    javax.swing.KeyStroke escKeyStroke = javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ESCAPE,0, false);
    getRootPane().getInputMap(javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW).put(escKeyStroke,"ESCAPE");
    javax.swing.Action escAction = new javax.swing.AbstractAction() {
        @Override public void actionPerformed(java.awt.event.ActionEvent e) {
            closeWindow();
        }};
    getRootPane().getActionMap().put("ESCAPE", escAction);
    //--- Alt-X eXit
    javax.swing.KeyStroke altXKeyStroke = javax.swing.KeyStroke.getKeyStroke(
            java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.ALT_MASK, false);
    getRootPane().getInputMap(javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW).put(altXKeyStroke,"ALT_X");
    getRootPane().getActionMap().put("ALT_X", escAction);
    //--- Alt-Q quit
    javax.swing.KeyStroke altQKeyStroke = javax.swing.KeyStroke.getKeyStroke(
            java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.ALT_MASK, false);
    getRootPane().getInputMap(javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW).put(altQKeyStroke,"ALT_Q");
    getRootPane().getActionMap().put("ALT_Q", escAction);
    //---- Position the window on the screen
    java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
    java.awt.Point frameLocation = new java.awt.Point(-1000,-1000);
    frameLocation.x = Math.max(0, (screenSize.width  - this.getWidth() ) / 2);
    frameLocation.y = Math.max(0, (screenSize.height - this.getHeight() ) / 2);
    this.setLocation(frameLocation);
    //---- Title, menus, etc
    this.setTitle("Database - Statistics");
    showJPanelTot(false);
    //---- Icon
    String iconName = "images/Sigma.gif";
    java.net.URL imgURL = this.getClass().getResource(iconName);
    if (imgURL != null) {this.setIconImage(new javax.swing.ImageIcon(imgURL).getImage());}
    else {Util.exceptn("Error: Could not load image = \""+iconName+"\"");}

  } //constructor
//</editor-fold>

  public void start() {
    setCursor(new java.awt.Cursor(java.awt.Cursor.WAIT_CURSOR));
    this.jButtonClose.setEnabled(false);
    this.setVisible(true);
    System.out.println(DataMaintenance.LINE+nl+"Starting \"Statistics\"");
    windowSize = this.getSize();
    if(pd.msgFrame != null) {
        pd.msgFrame.setParentFrame(Statistics.this);
        jCheckBoxDebugFrame.setSelected(pd.msgFrame.isVisible());
    }
    if(!startStatistics()) {closeWindow();}
  }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanelCheckBox = new javax.swing.JPanel();
        jCheckBoxDebugFrame = new javax.swing.JCheckBox();
        jButtonClose = new javax.swing.JButton();
        jLabelComps = new javax.swing.JLabel();
        jScrollPaneComps = new javax.swing.JScrollPane();
        jListComps = new javax.swing.JList();
        jPanelTot = new javax.swing.JPanel();
        jLabelTot = new javax.swing.JLabel();
        jLabelTotCmplx = new javax.swing.JLabel();
        jLabelN = new javax.swing.JLabel();
        jLabelTotComps = new javax.swing.JLabel();
        jLabelTotCompsFiles = new javax.swing.JLabel();
        jLabelNa = new javax.swing.JLabel();
        jLabelNaFiles = new javax.swing.JLabel();
        jSeparator1 = new javax.swing.JSeparator();
        jPanelErrs = new javax.swing.JPanel();
        jLabelErr = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        jLabelNameWoutNumb = new javax.swing.JLabel();
        jScrollNameWoutNumb = new javax.swing.JScrollPane();
        jListNameWoutNumb = new javax.swing.JList();
        jPanel2 = new javax.swing.JPanel();
        jLabelNumbWoutName = new javax.swing.JLabel();
        jScrollNumbWoutName = new javax.swing.JScrollPane();
        jListNumbWoutName = new javax.swing.JList();
        jPanel3 = new javax.swing.JPanel();
        jLabelHConflict = new javax.swing.JLabel();
        jScrollHConflict = new javax.swing.JScrollPane();
        jListHConflict = new javax.swing.JList();
        jPanel4 = new javax.swing.JPanel();
        jLabelChargeError = new javax.swing.JLabel();
        jScrollChargeError = new javax.swing.JScrollPane();
        jListChargeError = new javax.swing.JList();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        addWindowFocusListener(new java.awt.event.WindowFocusListener() {
            public void windowGainedFocus(java.awt.event.WindowEvent evt) {
                formWindowGainedFocus(evt);
            }
            public void windowLostFocus(java.awt.event.WindowEvent evt) {
            }
        });
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });

        jCheckBoxDebugFrame.setMnemonic('s');
        jCheckBoxDebugFrame.setText("show messages");
        jCheckBoxDebugFrame.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxDebugFrameActionPerformed(evt);
            }
        });

        jButtonClose.setMnemonic('c');
        jButtonClose.setText("Close");
        jButtonClose.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonCloseActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanelCheckBoxLayout = new javax.swing.GroupLayout(jPanelCheckBox);
        jPanelCheckBox.setLayout(jPanelCheckBoxLayout);
        jPanelCheckBoxLayout.setHorizontalGroup(
            jPanelCheckBoxLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelCheckBoxLayout.createSequentialGroup()
                .addComponent(jButtonClose)
                .addGap(52, 52, 52)
                .addComponent(jCheckBoxDebugFrame)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        jPanelCheckBoxLayout.setVerticalGroup(
            jPanelCheckBoxLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelCheckBoxLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jCheckBoxDebugFrame)
                .addComponent(jButtonClose))
        );

        jLabelComps.setLabelFor(jListComps);
        jLabelComps.setText("Components found:");

        jListComps.setModel(sortedModelCompsFnd);
        jListComps.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jListCompsFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                jListCompsFocusLost(evt);
            }
        });
        jScrollPaneComps.setViewportView(jListComps);

        jLabelTot.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N
        jLabelTot.setText("Totals");

        jLabelTotCmplx.setText("Number of reactions =");

        jLabelN.setText("0");

        jLabelTotComps.setText("Number of components =");

        jLabelTotCompsFiles.setText("Nbr comps. in files: ");

        jLabelNa.setText("0");

        jLabelNaFiles.setText("0");

        javax.swing.GroupLayout jPanelTotLayout = new javax.swing.GroupLayout(jPanelTot);
        jPanelTot.setLayout(jPanelTotLayout);
        jPanelTotLayout.setHorizontalGroup(
            jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelTotLayout.createSequentialGroup()
                .addGroup(jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabelTot)
                    .addGroup(jPanelTotLayout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(jPanelTotLayout.createSequentialGroup()
                                .addComponent(jLabelTotCmplx)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabelN))
                            .addGroup(jPanelTotLayout.createSequentialGroup()
                                .addComponent(jLabelTotComps)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabelNa))
                            .addGroup(jPanelTotLayout.createSequentialGroup()
                                .addComponent(jLabelTotCompsFiles)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabelNaFiles)))))
                .addContainerGap(39, Short.MAX_VALUE))
        );
        jPanelTotLayout.setVerticalGroup(
            jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelTotLayout.createSequentialGroup()
                .addComponent(jLabelTot)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelTotCmplx)
                    .addComponent(jLabelN))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelTotComps)
                    .addComponent(jLabelNa))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanelTotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelTotCompsFiles)
                    .addComponent(jLabelNaFiles))
                .addContainerGap(31, Short.MAX_VALUE))
        );

        jLabelErr.setFont(new java.awt.Font("Tahoma", 1, 14)); // NOI18N
        jLabelErr.setText("Errors:");

        javax.swing.GroupLayout jPanelErrsLayout = new javax.swing.GroupLayout(jPanelErrs);
        jPanelErrs.setLayout(jPanelErrsLayout);
        jPanelErrsLayout.setHorizontalGroup(
            jPanelErrsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelErrsLayout.createSequentialGroup()
                .addComponent(jLabelErr)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        jPanelErrsLayout.setVerticalGroup(
            jPanelErrsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelErrsLayout.createSequentialGroup()
                .addComponent(jLabelErr)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jLabelNameWoutNumb.setLabelFor(jListNameWoutNumb);
        jLabelNameWoutNumb.setText("<html>Reactions with a<br>component and<br>no stoich.coef.</html>");

        jListNameWoutNumb.setModel(sortedModelNameWoutNumber);
        jListNameWoutNumb.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jListNameWoutNumbFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                jListNameWoutNumbFocusLost(evt);
            }
        });
        jScrollNameWoutNumb.setViewportView(jListNameWoutNumb);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollNameWoutNumb, javax.swing.GroupLayout.DEFAULT_SIZE, 127, Short.MAX_VALUE)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addComponent(jLabelNameWoutNumb, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addComponent(jLabelNameWoutNumb, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollNameWoutNumb, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
        );

        jLabelNumbWoutName.setLabelFor(jListNumbWoutName);
        jLabelNumbWoutName.setText("<html>Reactions with a<br>stoich.coef. and<br>no component</html>");

        jListNumbWoutName.setModel(sortedModelNumberWoutName);
        jListNumbWoutName.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jListNumbWoutNameFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                jListNumbWoutNameFocusLost(evt);
            }
        });
        jScrollNumbWoutName.setViewportView(jListNumbWoutName);

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollNumbWoutName, javax.swing.GroupLayout.PREFERRED_SIZE, 126, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabelNumbWoutName, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 0, 0))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addComponent(jLabelNumbWoutName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollNumbWoutName, javax.swing.GroupLayout.DEFAULT_SIZE, 124, Short.MAX_VALUE))
        );

        jLabelHConflict.setLabelFor(jListHConflict);
        jLabelHConflict.setText("<html>Reactions having<br>proton-nbr. in conflict<br>with H+ stoich.coef.</html>");

        jListHConflict.setModel(sortedModelHConflict);
        jListHConflict.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jListHConflictFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                jListHConflictFocusLost(evt);
            }
        });
        jScrollHConflict.setViewportView(jListHConflict);

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabelHConflict, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jScrollHConflict, javax.swing.GroupLayout.PREFERRED_SIZE, 125, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 0, 0))
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addComponent(jLabelHConflict, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollHConflict, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
        );

        jLabelChargeError.setLabelFor(jListChargeError);
        jLabelChargeError.setText("<html>Reactions with<br>charge<br>imbalance</html>");

        jListChargeError.setModel(sortedModelChargeError);
        jListChargeError.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jListChargeErrorFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                jListChargeErrorFocusLost(evt);
            }
        });
        jScrollChargeError.setViewportView(jListChargeError);

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel4Layout.createSequentialGroup()
                .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabelChargeError, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jScrollChargeError, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 0, 0))
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel4Layout.createSequentialGroup()
                .addComponent(jLabelChargeError, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollChargeError, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jSeparator1)
                    .addComponent(jPanelErrs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addComponent(jPanelCheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jScrollPaneComps, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jLabelComps))
                                .addGap(18, 18, 18)
                                .addComponent(jPanelTot, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanelCheckBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabelComps)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jScrollPaneComps, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jPanelTot, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanelErrs, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

//<editor-fold defaultstate="collapsed" desc="Events">

    private void jButtonCloseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCloseActionPerformed
        closeWindow();
    }//GEN-LAST:event_jButtonCloseActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        closeWindow();
    }//GEN-LAST:event_formWindowClosing

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
      if(windowSize != null) {
        int w = Math.round((float)windowSize.getWidth());
        int h = Math.round((float)windowSize.getHeight());
        if(this.getHeight()<h){this.setSize(this.getWidth(), h);}
        if(this.getWidth()<w){this.setSize(w,this.getHeight());}
      }
    }//GEN-LAST:event_formComponentResized

    private void jCheckBoxDebugFrameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxDebugFrameActionPerformed
        if(pd.msgFrame != null) {pd.msgFrame.setVisible(jCheckBoxDebugFrame.isSelected());}
    }//GEN-LAST:event_jCheckBoxDebugFrameActionPerformed

    private void jListCompsFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListCompsFocusGained
        jScrollPaneComps.setBorder(highlightedBorder);
        if(modelCompsFnd.getSize()>0) {
            int i = Math.max(0,jListComps.getSelectedIndex());
            jListComps.setSelectedIndex(i);
            jListComps.ensureIndexIsVisible(i);
        }
    }//GEN-LAST:event_jListCompsFocusGained

    private void jListCompsFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListCompsFocusLost
        jScrollPaneComps.setBorder(defBorder);
    }//GEN-LAST:event_jListCompsFocusLost

    private void jListNameWoutNumbFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListNameWoutNumbFocusGained
        jScrollNameWoutNumb.setBorder(highlightedBorder);
        if(modelNameWoutNumber.getSize()>0) {
            int i = Math.max(0,jListNameWoutNumb.getSelectedIndex());
            jListNameWoutNumb.setSelectedIndex(i);
            jListNameWoutNumb.ensureIndexIsVisible(i);
        }
    }//GEN-LAST:event_jListNameWoutNumbFocusGained

    private void jListNameWoutNumbFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListNameWoutNumbFocusLost
        jScrollNameWoutNumb.setBorder(defBorder);
    }//GEN-LAST:event_jListNameWoutNumbFocusLost

    private void jListNumbWoutNameFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListNumbWoutNameFocusGained
        jScrollNumbWoutName.setBorder(highlightedBorder);
        if(modelNumberWoutName.getSize()>0) {
            int i = Math.max(0,jListNumbWoutName.getSelectedIndex());
            jListNumbWoutName.setSelectedIndex(i);
            jListNumbWoutName.ensureIndexIsVisible(i);
        }
    }//GEN-LAST:event_jListNumbWoutNameFocusGained

    private void jListNumbWoutNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListNumbWoutNameFocusLost
        jScrollNumbWoutName.setBorder(defBorder);
    }//GEN-LAST:event_jListNumbWoutNameFocusLost

    private void jListHConflictFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListHConflictFocusGained
        jScrollHConflict.setBorder(highlightedBorder);
        if(modelHConflict.getSize()>0) {
            int i = Math.max(0, jListHConflict.getSelectedIndex());
            jListHConflict.setSelectedIndex(i);
            jListHConflict.ensureIndexIsVisible(i);
        }
    }//GEN-LAST:event_jListHConflictFocusGained

    private void jListHConflictFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListHConflictFocusLost
        jScrollHConflict.setBorder(defBorder);
    }//GEN-LAST:event_jListHConflictFocusLost

    private void jListChargeErrorFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListChargeErrorFocusGained
        jScrollChargeError.setBorder(highlightedBorder);
        if(modelChargeError.getSize()>0) {
            int i = Math.max(0,jListChargeError.getSelectedIndex());
            jListChargeError.setSelectedIndex(i);
            jListChargeError.ensureIndexIsVisible(i);
        }
    }//GEN-LAST:event_jListChargeErrorFocusGained

    private void jListChargeErrorFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jListChargeErrorFocusLost
        jScrollChargeError.setBorder(defBorder);
    }//GEN-LAST:event_jListChargeErrorFocusLost

    private void formWindowGainedFocus(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowGainedFocus
        if(pd.msgFrame != null) {jCheckBoxDebugFrame.setSelected(pd.msgFrame.isVisible());}
        else {jCheckBoxDebugFrame.setEnabled(false);}
    }//GEN-LAST:event_formWindowGainedFocus
//</editor-fold>

//<editor-fold defaultstate="collapsed" desc="Methods">
  private void closeWindow() {
    finished = true;    //return from "waitFor()"
    this.notify_All();
    this.dispose();
  } // closeWindow()
  /** this method will wait for this window to be closed */
  public synchronized void waitFor() {
    while(!finished) {
        try {wait();} catch (InterruptedException ex) {}
    } // while
  } // waitFor()
  private synchronized void notify_All() { //needed by "waitFor()"
      notifyAll();
  }
  private static Object makeObj(final String item)  {
     return new Object() {@Override public String toString() { return item; } };
  }

  //<editor-fold defaultstate="collapsed" desc="showJPanelTot">
  private void showJPanelTot(boolean show) {
    if(show) {
        jLabelTot.setText("Totals:");
        jLabelTotCmplx.setText("Number of reactions =");
        jLabelN.setText("0");
        jLabelTotComps.setText("Number of components =");
        jLabelNa.setText("0");
        jLabelTotCompsFiles.setText("Nbr comps. in files: ");
        jLabelNaFiles.setText("0");
        jListComps.setEnabled(true);
        jListNameWoutNumb.setEnabled(true);
        jListNumbWoutName.setEnabled(true);
        jListHConflict.setEnabled(true);
        jListChargeError.setEnabled(true);
    } else {
        jLabelTot.setText(" ");
        jLabelTotCmplx.setText(" ");
        jLabelN.setText(" ");
        jLabelTotComps.setText(" ");
        jLabelNa.setText(" ");
        jLabelTotCompsFiles.setText(" ");
        jLabelNaFiles.setText(" ");
        jListComps.setEnabled(false);
        jListNameWoutNumb.setEnabled(false);
        jListNumbWoutName.setEnabled(false);
        jListHConflict.setEnabled(false);
        jListChargeError.setEnabled(false);
    }
  } //showJPanelTot(show)
//</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="reactString(Complex)">
  /** Returns a String representation of the reaction such as:<pre>
   * "<code>Fe 2+;1;;;e-;-1;;;;;;;0;</code>"</pre>
   * Equivalent to <code>Complex.toString</code>, except that (1) the product name,
   * the logK and the reference are excluded, and (2) the reactants are sorted.
   * That is, it returns only the reaction (sorted).
   * If the product name starts with "@" this method returns an empty String ("").
   * @param cmplx the complex
   * @return a text representing the reaction
   * @see Complex#toString() Complex.toString  */
    private static String reactString(Complex cmplx) {
    if(cmplx == null) {return "";}
    //if(cmplx.name.startsWith("@")) {return text.toString();}
    if(cmplx.name.startsWith("@")) {return "";}
    Complex c;
    try {c = (Complex)cmplx.clone();}
    catch(CloneNotSupportedException cex) {return "";}
    // -- exclude water?
    //for(int ic =0; ic < Complex.NDIM; ic++) {
    //  if(Util.isWater(c.component[ic])) {c.component[ic] = ""; c.numcomp[ic] = 0; break;}
    //}
    StringBuilder text = new StringBuilder();  
    //text.append(Complex.encloseInQuotes(c.name)); text.append(";");
    Complex.sortReactants(c);
    for(int ic =0; ic < Complex.NDIM; ic++) {
      if(c.component[ic] == null || c.component[ic].length()<=0
              // || Util.isWater(c.component[ic])
              || Math.abs(c.numcomp[ic]) < 0.001) {text.append(";;"); continue;}
      text.append(Complex.encloseInQuotes(c.component[ic])); text.append(";");
      text.append(Util.formatDbl4(c.numcomp[ic]).trim()); text.append(";");
    }
    if(Math.abs(c.proton) >=0.001) {text.append(Util.formatDbl4(c.proton).trim());}
    text.append(";");
    return text.toString();
  } //reactString(cmplx)
//</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="startStatistics">
 /** Performs the "real" work: checks and statistics
  * 
  * @return false if the procedure ends "unexpectedly": the user selects to quit
  */
  private boolean startStatistics() {
      // ----- get an output file name
      String dir = pc.pathDef.toString();
      if(dir.endsWith(SLASH)) {dir = dir.substring(0, dir.length()-1);}
      final String LOGfileName = Util.getSaveFileName(this, pc.progName,
                "Select an output file:", 7,
                dir + SLASH + "Statistics.txt", null);
      if(LOGfileName == null || LOGfileName.trim().length() <=0) {return false;}
      final java.io.File tmpFile = new java.io.File(LOGfileName);
      pc.setPathDef(tmpFile);
      outputFile = null;
      try {
          outputFile = new java.io.PrintWriter(
                new java.io.BufferedWriter(
                new java.io.FileWriter(tmpFile)));
      }
      catch (java.io.IOException ex) {
          String msg = "Error \""+ex.getMessage()+"\","+nl+
                    "   while making a PrintWriter for file:"+nl+
                    "   \""+LOGfileName+"\"";
          Util.exceptn(msg);
          javax.swing.JOptionPane.showMessageDialog(this, msg, pc.progName,
                                    javax.swing.JOptionPane.ERROR_MESSAGE);
          if(outputFile != null) {outputFile.close();}
          return false;
      }
      itemsCompsFnd.clear();
      modelCompsFnd.clear();
      modelNameWoutNumber.clear();
      modelNumberWoutName.clear();
      modelHConflict.clear();
      modelChargeError.clear();

      showJPanelTot(true);
      System.out.println("Output file: "+LOGfileName);
      // ----- read the list of database files
      // readIni(jTextFieldINI.getText());
      //---- read the elements/components for the databases
      LibDB.getElements(this, pc, pd.dataBasesList, pd.elemComp);
      //the number of unique reactants in pd.elemComp
      na = pd.elemComp.size()+1;
      for(int i=0; i < (pd.elemComp.size()-1); i++) {
        for(int j=(i+1); j < pd.elemComp.size(); j++) {
            if(pd.elemComp.get(i)[1].equals(pd.elemComp.get(j)[1])) {na--;}
        }
      }
      jLabelNaFiles.setText(String.valueOf(na));
      // ----- scan the files
      outputFile.println("Statistics for LogK databases:"+nl+nl+"Databases for reactions:");
      java.io.File f;
      String name;
      java.text.DateFormat df = java.text.DateFormat.getDateTimeInstance(java.text.DateFormat.DEFAULT,
                                   java.text.DateFormat.SHORT);
      for(int i=0; i < pd.dataBasesList.size(); i++) {
        name = pd.dataBasesList.get(i).toString().trim();
        if(name.length() >0) {
            f = new java.io.File(name);
            outputFile.println("  "+f.getAbsolutePath()+"   "+df.format(f.lastModified()));
        }
      }//for i
      outputFile.println(nl+"Element-reactant files:");
      for(int i=0; i < pd.dataBasesList.size(); i++) {
        name = pd.dataBasesList.get(i).toString().trim();
        if(name.length() >0) {
            f = new java.io.File(Div.getFileNameWithoutExtension(name)+".elt");
            if(!f.exists()) {f = new java.io.File(Div.getFileNameWithoutExtension(name)+".elb");}
            if(f.exists()) {outputFile.println("  "+f.getAbsolutePath()+"   "+df.format(f.lastModified()));}
        }
      }//for i

      final LibSearch libS;
      try{libS = new LibSearch(pd);}
      catch (LibSearch.LibSearchException ex) {
        Util.exceptn(ex.toString()); return false;
      }

      this.setCursor(new java.awt.Cursor(java.awt.Cursor.WAIT_CURSOR));
      this.jButtonClose.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));

      new javax.swing.SwingWorker<Void,Void>() {
      @Override protected Void doInBackground() throws Exception {
        boolean fistComplex = true;
        nx =0; 
        int noName =0;
        int nH, i, j;
        boolean fnd, ok, exception = false;
        boolean isRedoxReaction;
        Complex cmplx;

        // a list of all product-reaction instances
        java.util.HashSet<String []> productsReactionsSet = new java.util.HashSet<String []>();
        // reaction products present with different reactions
        java.util.TreeSet<String> duplProductsSet = new java.util.TreeSet<String>();
        // duplicate reactions (with the same reaction product)
        java.util.TreeSet<String> duplReactionsSameProdctSet = new java.util.TreeSet<String>();
        // duplicate reactions (with different reaction product)
        java.util.TreeSet<String> duplReactionsDifProductSet = new java.util.TreeSet<String>();
        // duplicate solids (with different ending: "(s)" and "(cr)")
        java.util.TreeSet<String> duplSolidsSet = new java.util.TreeSet<String>();
        // product names not containing one or more reactant names, for example Fe+2 + Cl- = ZnCl+
        java.util.ArrayList<String> itemsNames = new java.util.ArrayList<String>();
        // list of reference citations not matching any reference
        java.util.ArrayList<String> itemsRefsNotFnd = null;
        // list of reference keys that have been cited
        java.util.ArrayList<String> itemsRefsFnd = null;
        java.util.ArrayList<String> rfs;
        if(pd.references != null)  {
            itemsRefsNotFnd = new java.util.ArrayList<String>();
            itemsRefsFnd = new java.util.ArrayList<String>();
        }
        int names = 0;
        String product, reaction;

        // -- loop through all reactions
        while(true) {
          if(finished) {
              outputFile.close();
              tmpFile.delete();
              return null;}
          try {cmplx = libS.getComplex(fistComplex);}
          catch (LibSearch.LibSearchException ex) {
            String msg = ex.getMessage()+nl+nl+"Canceling the operation.";
            Util.exceptn(msg);
            javax.swing.JOptionPane.showMessageDialog(Statistics.this, msg, pc.progName,
                                    javax.swing.JOptionPane.ERROR_MESSAGE);
            exception = true;
            break;
          }
          if(cmplx == null) {break;}
          fistComplex = false;
          nx++;
          javax.swing.SwingUtilities.invokeLater(new Runnable() {@Override public void run() {
            jLabelN.setText(String.valueOf(nx));
          }});
          if(cmplx.reference !=null && cmplx.reference.trim().length()>0
                  && pd.references !=null && itemsRefsNotFnd != null) {
              rfs = pd.references.splitRefs(cmplx.reference);
              for(String r : rfs) {
                if(pd.references.isRefThere(r) == null){
                    if(!itemsRefsNotFnd.contains(r)) {itemsRefsNotFnd.add(r);}
                } else {
                    if(itemsRefsFnd != null && !itemsRefsFnd.contains(r)) {itemsRefsFnd.add(r);}
                }
              }
          }
          if(cmplx.name == null || cmplx.name.trim().length() <=0) {noName++; continue;}
          if(cmplx.name.startsWith("@")) {product = cmplx.name.substring(1);} else {product = cmplx.name;}
          reaction = reactString(cmplx); // if it starts with "@" the reaction is "" (empty)
          isRedoxReaction = Complex.isRedox(cmplx);

          // -- find out duplicate reactions (with the same reaction product)
          //    empty reactions are not compared
          for(String[] r : productsReactionsSet) {
              if(r[1].length() > 0 && Util.stringsEqual(r[1], reaction)) {
                  if(Util.nameCompare(product, r[0])) {duplReactionsSameProdctSet.add(product);}
              }
          }
          for(String[] r : productsReactionsSet) {
               // (empty reactions are not compared).
              if(r[1].length() > 0 && Util.stringsEqual(r[1], reaction)) {
                  // -- find out duplicate reactions (with different reaction product)
                  //    Names that will not give a warning:
                  //        Fe(OH)2(s) and Fe(OH)2(cr) and Fe(OH)2
                  //        CO2 and CO2(g)
                  //    but Fe(c) and Fe(cr) will give a warning
                  if(// both are solid but not equal, e.g. AmCO3OH(s) and AmOHCO3(cr)
                     (Util.isSolid(product) && Util.isSolid(r[0]) && 
                          !Util.bareNameOf(product).equals(Util.bareNameOf(r[0])))
                     // both are solid and equal except for "(c)" and "(cr)"
                     || (Util.is_cr_or_c_solid(product) && Util.is_cr_or_c_solid(r[0])
                          && Util.bareNameOf(product).equals(Util.bareNameOf(r[0])))
                     // both gas but different, e.g. H2S(g) and SH2(g)
                     || (Util.isGas(product) && Util.isGas(r[0]) && 
                          !Util.bareNameOf(product).equals(Util.bareNameOf(r[0])))
                     // none is solid or gas, but different, such as VO2(OH)2- and VO3-
                     || (!Util.isSolid(product) && !Util.isSolid(r[0]) &&
                         !Util.isGas(product) && !Util.isGas(r[0]) &&
                                !Util.nameCompare(product, r[0]))) {
                      duplReactionsDifProductSet.add(product+"   and:  "+r[0]);
                  }
                  // -- find out duplicate solids (with different phase designation)
                  if(Util.isSolid(product) && Util.isSolid(r[0])
                          && Util.bareNameOf(product).equals(Util.bareNameOf(r[0]))) {
                      if(product.endsWith("(am)")) {
                            duplSolidsSet.add(r[0]+"   and:  "+product);
                      } else {
                            duplSolidsSet.add(product+"   and:  "+r[0]);
                      }
                  }
              }
          }
          // -- find out duplicate reaction products having different reactions
          for(String[] r : productsReactionsSet) {
              if(Util.nameCompare(product, r[0])) {
                  if(!isRedoxReaction
                          && !(r[1].length() > 0 && Util.stringsEqual(r[1], reaction))) {
                      duplProductsSet.add(product);
                  }
              }
          }
          // -- keep a list of all products/reactions
          productsReactionsSet.add(new String[]{cmplx.name,reaction});
          // --
          if(cmplx.name.startsWith("@")) {continue;}
          // -- list all reactions with a reactant given with no coefficients
          final Object o = makeObj(cmplx.name);
          for(i=0; i<Complex.NDIM; i++) {
              if(cmplx.component[i] != null && cmplx.component[i].trim().length() >0) {
                  if(Math.abs(cmplx.numcomp[i]) < 0.001) {
                    javax.swing.SwingUtilities.invokeLater(new Runnable() {@Override public void run() {
                        modelNameWoutNumber.addElement(o);
                    }});
                    break;
                  }
              }
          } //for i
          // -- find reactions with a coefficnet with no reactant name
          for(i=0; i<Complex.NDIM; i++) {
              if(Math.abs(cmplx.numcomp[i]) >= 0.001
                      && (cmplx.component[i] == null || cmplx.component[i].trim().length() <=0)) {
                  javax.swing.SwingUtilities.invokeLater(new Runnable() {@Override public void run() {
                    modelNumberWoutName.addElement(o);
                  }});
                  break;
              }
          } //for i          
          // -- find reactions not charge balanced
          if(!Complex.isChargeBalanced(cmplx)) {
            javax.swing.SwingUtilities.invokeLater(new Runnable() {@Override public void run() {
                modelChargeError.addElement(o);
            }});
          }

          // -- list reactants; keep track of H+
          nH = -1;
          for(i =0; i < Complex.NDIM; i++) {
              if(cmplx.component[i] == null || cmplx.component[i].trim().length() <=0) {continue;}
              if(Util.isProton(cmplx.component[i])) {nH = i;}
              if(itemsCompsFnd.containsKey(cmplx.component[i])) {
                  j = itemsCompsFnd.get(cmplx.component[i]);
                  itemsCompsFnd.put(cmplx.component[i],j+1);
              } else {
                  itemsCompsFnd.put(cmplx.component[i],1);
                  final Object oC = makeObj(cmplx.component[i]);
                  javax.swing.SwingUtilities.invokeLater(new Runnable() {@Override public void run() {
                    modelCompsFnd.addElement(oC);
                    jLabelNa.setText(String.valueOf(modelCompsFnd.size()));
                  }});
              }
          } //for i

          // -- find reactions with error in H+
          if(nH >=0 && !Util.areEqualDoubles(cmplx.proton, cmplx.numcomp[nH])) {
            javax.swing.SwingUtilities.invokeLater(new Runnable() {@Override public void run() {
                modelHConflict.addElement(o);
            }});
          }
          // -- find out if the reactants are in the name of the product
          //<editor-fold defaultstate="collapsed" desc="is reactant in product name?">
          fnd = true;
          for(i =0; i < Complex.NDIM; i++) {
              String t = cmplx.component[i];
              if(t == null || t.length() <=0) {continue;}
              if(Util.isElectron(t) || Util.isProton(t) || Util.isWater(t))  {continue;}
              t = Util.bareNameOf(t);
              ok = cmplx.name.contains(t);
              if(!ok && t.equals("Hg2")) {ok = cmplx.name.contains("Hg");}
              if(!ok && t.equals("CH3Hg")) {ok = (cmplx.name.contains("Hg") && cmplx.name.contains("CH"));}
              if(!ok && t.equals("NH3")) {ok = cmplx.name.contains("NH");}
              if(!ok && t.equals("NH3") && isRedoxReaction) {ok = cmplx.name.contains("N");}
              if(!ok && (t.equals("NO2") || t.equals("NO3"))) {ok = cmplx.name.contains("N");}
              if(!ok && (t.equals("CO3") || t.equals("HCOO"))) {ok = cmplx.name.contains("C");}
              if(!ok && (t.equals("MoO4") || t.equals("Mo2O2")
                      || t.equals("Mo2O4") || t.equals("Mo2(OH)2"))) {ok = cmplx.name.contains("Mo");}
              if(!ok && t.equals("WO4")) {ok = cmplx.name.contains("W");}
              if(!ok && t.equals("CrO4")) {ok = cmplx.name.contains("Cr");}
              if(!ok && t.equals("VO2")) {ok = cmplx.name.contains("V");}
              if(!ok && t.equals("UO2")) {ok = cmplx.name.contains("U");}
              if(!ok && t.equals("AmO2")) {ok = cmplx.name.contains("Am");}
              if(!ok && t.equals("NpO2")) {ok = cmplx.name.contains("Np");}
              if(!ok && t.equals("PuO2")) {ok = cmplx.name.contains("Pu");}
              if(!ok && t.equals("TcO(OH)2")) {ok = cmplx.name.contains("Tc");}
              if(!ok && (t.equals("As(OH)3") || t.equals("AsO4")
                      || t.equals("H3AsO3"))) {ok = cmplx.name.contains("As");}
              if(!ok && (t.equals("Sb(OH)3") || t.equals("Sb(OH)6"))) {ok = cmplx.name.contains("Sb");}
              if(!ok && (t.equals("Ge(OH)4") || t.equals("Ge(OH)2"))) {ok = cmplx.name.contains("Ge");}
              if(!ok && (t.equals("Te(OH)4") || t.equals("Te(OH)6") || t.equals("HTe"))) {ok = cmplx.name.contains("Te");}
              if(!ok && (t.contains("TeO3") || t.contains("TeO4") || t.contains("TeO6"))) {ok = cmplx.name.contains("Te");}
              if(!ok && t.equals("Ta(OH)5")) {ok = cmplx.name.contains("Ta");}
              if(!ok && t.equals("Nb(OH)5")) {ok = cmplx.name.contains("Nb");}
              if(!ok && t.equals("PoO3")) {ok = cmplx.name.contains("Po");}
              if(!ok && t.equals("Si(OH)4")) {ok = cmplx.name.contains("Si");}
              if(!ok && t.equals("OsO4")) {ok = cmplx.name.contains("Os");}
              if(!ok && t.equals("TcO4")) {ok = cmplx.name.contains("Tc");}
              if(!ok && t.equals("ReO4")) {ok = cmplx.name.contains("Re");}
              if(!ok && (t.equals("RuO4") || t.equals("Ru(OH)2"))) {ok = cmplx.name.contains("Ru");}
              if(!ok && t.equals("TiO")) {ok = cmplx.name.contains("Ti");}
              if(!ok && t.equals("VO")) {ok = cmplx.name.contains("V");}
              if(!ok && t.equals("B(OH)3")) {ok = cmplx.name.contains("B");}
              if(!ok && (t.equals("PO4") || t.equals("H2PO2")
                      || t.equals("HPO3") || t.equals("P2O6"))) {ok = cmplx.name.contains("P");}
              if(!ok && t.equals("CNO")) {ok = cmplx.name.contains("CN");}
              if(!ok && (t.equals("BrO") || t.equals("BrO3"))) {ok = cmplx.name.contains("Br");}
              if(!ok && t.equals("IO3")) {ok = cmplx.name.contains("I");}
              if(!ok && t.equals("H2O2")) {ok = cmplx.name.contains("O2");}
              if(!ok && (t.equals("H2Se") || t.equals("HSe") || t.equals("SeO4") || t.equals("SeO3"))) {ok = cmplx.name.contains("Se");}
              if(!ok && t.equals("HS")) {ok = cmplx.name.contains("S");}
              if(!ok && (t.equals("SO4") || t.equals("SO3") || t.equals("S2O3"))) {ok = cmplx.name.contains("S");}
              if(!ok) {
                  fnd = false;
                  itemsNames.add(cmplx.name+"  does not contain  "+cmplx.component[i]);
              }
          } //for i
          if(!fnd) {names++;}
          //</editor-fold>
        } // while
        if(exception) {
            outputFile.flush();
            outputFile.close();
            tmpFile.delete();
            finished = true;
            return null;
        }
        // itemsRedox = null;  // garbage collect
        // itemsReactn = null; // garbage collect
        outputFile.println(nl+"Total nbr reactions = "+nx);
        outputFile.println("Total nbr reactants found in reaction databases = "+modelCompsFnd.size()+nl+
                "      (nbr reactants in element-reactant files = "+na+")"+nl);
        if(modelCompsFnd.size()>0) {
            outputFile.println(
                "Reactants found in reaction databases:"+nl+
                "  list  &  Nbr of reactions they participate in");
            for(i =0; i < modelCompsFnd.size(); i++) {
                String t = sortedModelCompsFnd.getElementAt(i).toString();
                j = itemsCompsFnd.get(t);
                if(t.length() <=20) {
                    outputFile.format("%4d  %-20s   %d", (i+1), t, j);
                    outputFile.println();
                } else {
                    outputFile.format("%4d  %s   %d", (i+1), t, j);
                    outputFile.println();
                }
            } //for i
        }
        // --
        boolean needsTitle = true;
        for(j=0; j<modelCompsFnd.size(); j++) {
            fnd = false;
            for(i=0; i < pd.elemComp.size(); i++) {
                if(pd.elemComp.get(i)[1].equals(modelCompsFnd.get(j).toString())) {fnd = true; break;}
            } //for i
            if(!fnd) {
                if(needsTitle) {
                    needsTitle = false;
                    outputFile.println(nl+"Components in databases for reactions"+nl+
                                          "   not present in element-reactant files:");
                }
                outputFile.println("      "+modelCompsFnd.get(j).toString());
            }
        } //for j
        // --
        needsTitle = true;
        for(i=0; i < pd.elemComp.size(); i++) {
            fnd = false;
            for(j=0; j<modelCompsFnd.size(); j++) {
                if(pd.elemComp.get(i)[1].equals(modelCompsFnd.get(j).toString())) {fnd = true; break;}
            } //for j
            if(!fnd) {
                if(needsTitle) {
                    needsTitle = false;
                    outputFile.println(nl+"Components in element-reactant files"+nl+
                                          "   not used in the databases for reactions:");
                }
                outputFile.println("      "+pd.elemComp.get(i)[1]);
            }
        } //for i
        // --
        java.util.ArrayList<String> items = new java.util.ArrayList<String>();
        for(j=0; j<(modelCompsFnd.size()-1); j++) {
            for(i=(j+1); i < modelCompsFnd.size(); i++) {
                if(Util.nameCompare(modelCompsFnd.get(j).toString(),modelCompsFnd.get(i).toString())) {
                    items.add(modelCompsFnd.get(j).toString()+"   and:  "+modelCompsFnd.get(i).toString());
                }
            }
        }
        if(items.size() >0) {
            outputFile.println(nl+"Errors in names of reactants in the databases"+nl+
                    "Names that are equivalent but will be treated as different:");
            for(String t : items) {outputFile.println("      "+t);}
        }
        items.clear();
        for(j=0; j<(pd.elemComp.size()-1); j++) {
            for(i=(j+1); i < pd.elemComp.size(); i++) {
                if(pd.elemComp.get(j)[1].equals(pd.elemComp.get(i)[1])) {continue;}
                if(Util.nameCompare(pd.elemComp.get(j)[1],pd.elemComp.get(i)[1])) {
                    items.add(pd.elemComp.get(j)[1]+"   and:  "+pd.elemComp.get(i)[1]);
                }
            }
        }
        if(items.size() >0) {
            outputFile.println(nl+"Errors in names of reactants in the element-reactant files"+nl+
                    "Names that are equivalent but will be treated as different:");
            for(String t : items) {outputFile.println("      "+t);}
        }
        // items = null; // gabrage collect?
        // --
        if(noName >0) {
            outputFile.println(nl+"Nbr of empty names for reaction products = "+noName);
        }
        if(modelNameWoutNumber.size() >0) {
            outputFile.println(nl+"Nbr of reactions having a reactant with name but without its stoich.coeff = "+modelNameWoutNumber.size());
            for(i=0; i<modelNameWoutNumber.size(); i++) {
                outputFile.println("  "+sortedModelNameWoutNumber.getElementAt(i).toString());
            }
        }
        if(modelNumberWoutName.size() >0) {
            outputFile.println(nl+"Nbr of reactions having a stoich.coeff with no reactant = "+modelNumberWoutName.size());
            for(i=0; i<modelNumberWoutName.size(); i++) {
                outputFile.println("  "+sortedModelNumberWoutName.getElementAt(i).toString());
            }
        }
        if(modelHConflict.size() >0) {
            outputFile.println(nl+"Nbr of reactions having a confict between stoich.coeff for H+"+nl+
                                  "    and the nbr-protons = "+modelHConflict.size());
            for(i=0; i<modelHConflict.size(); i++) {
                outputFile.println("  "+sortedModelHConflict.getElementAt(i).toString());
            }
        }
        if(modelChargeError.size() >0) {
            outputFile.println(nl+"Nbr of reactions with charge imbalance = "+modelChargeError.size());
            for(i=0; i<modelChargeError.size(); i++) {
                outputFile.println("  "+sortedModelChargeError.getElementAt(i).toString());
            }
        }
        if(duplReactionsSameProdctSet.size() > 0) {
            outputFile.println(nl+"Nbr of reactions found more than once"+nl+
                                  "    (with the same product) = "+duplReactionsSameProdctSet.size());
            for(String t : duplReactionsSameProdctSet) {outputFile.println("   "+t);}
        }
        if(duplReactionsDifProductSet.size() > 0) {
            outputFile.println(nl+"Nbr of reactions found more than once"+nl+
                                  "    (with a different product) = "+duplReactionsDifProductSet.size());
            for(String t : duplReactionsDifProductSet) {outputFile.println("   "+t);}
        }
        if(duplSolidsSet.size() > 0) {
            outputFile.println(nl+"Nbr of solids found more than once"+nl+
                                  "    (with different phase designator) = "+duplSolidsSet.size());
            for(String t : duplSolidsSet) {outputFile.println("   "+t);}
        }        
        if(duplProductsSet.size() > 0) {
            outputFile.println(nl+"Nbr of reaction products found more than once"+nl+
                                  "    (with a different reaction) = "+duplProductsSet.size());
            for(String t : duplProductsSet) {outputFile.println("   "+t);}
        }
        // itemsDupl = null; // garbage collect
        if(itemsNames.size() >0) {
            java.util.Collections.sort(itemsNames,String.CASE_INSENSITIVE_ORDER);
            outputFile.println(nl+"Nbr of reaction products where the name"+nl+
                                  "    does not contain one or more reactant(s) = "+names);
            for(String t : itemsNames) {outputFile.println("   "+t);}
        }
        // -- References
        if(pd.references != null) {
            if(itemsRefsNotFnd != null && !itemsRefsNotFnd.isEmpty()) {
                java.util.Collections.sort(itemsRefsNotFnd,String.CASE_INSENSITIVE_ORDER);
                outputFile.println(nl+"Nbr of references not found = "+itemsRefsNotFnd.size());
                for(String t : itemsRefsNotFnd) {outputFile.println("   "+t);}
            }
            if(itemsRefsFnd != null && !itemsRefsFnd.isEmpty()) {
                itemsNames.clear();
                Object[] keys = pd.references.referenceKeys();
                for (Object k : keys) {
                    if(k != null) {
                        ok = false;
                        String t = k.toString().trim();
                        for(String t2 : itemsRefsFnd) {
                            if(t2 != null && t2.equalsIgnoreCase(t)) {ok = true; break;}
                        }
                        if(!ok) {itemsNames.add(t);}
                    }
                } // for Object k
                if(itemsNames.size() >0) {
                    java.util.Collections.sort(itemsNames,String.CASE_INSENSITIVE_ORDER);
                    outputFile.println(nl+"References not used:");
                    for(String t : itemsNames) {outputFile.println("   "+t);}
                }
            } //if !itemsRefsFnd.isEmpty()
        }// references
        // -- close
        outputFile.flush();
        outputFile.close();
        if(finished) {tmpFile.delete();}
        return null;
      } // doInBackground()
      @Override protected void done(){
        // ---- The end
        Statistics.this.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        if(finished) {Statistics.this.closeWindow(); return;}
        String msg = "Written file:"+nl+"   \""+LOGfileName+"\"";
        System.out.println(msg+nl+"\"Statistics\" finished.");
        javax.swing.JOptionPane.showMessageDialog(Statistics.this,msg,
                    pc.progName,javax.swing.JOptionPane.INFORMATION_MESSAGE);
        jButtonClose.setEnabled(true);
        jButtonClose.requestFocusInWindow();
      } // done()
      }.execute();
      return true; // this returns inmediately
  } //startStatistics
//</editor-fold>

//</editor-fold>


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButtonClose;
    private javax.swing.JCheckBox jCheckBoxDebugFrame;
    private javax.swing.JLabel jLabelChargeError;
    private javax.swing.JLabel jLabelComps;
    private javax.swing.JLabel jLabelErr;
    private javax.swing.JLabel jLabelHConflict;
    private javax.swing.JLabel jLabelN;
    private javax.swing.JLabel jLabelNa;
    private javax.swing.JLabel jLabelNaFiles;
    private javax.swing.JLabel jLabelNameWoutNumb;
    private javax.swing.JLabel jLabelNumbWoutName;
    private javax.swing.JLabel jLabelTot;
    private javax.swing.JLabel jLabelTotCmplx;
    private javax.swing.JLabel jLabelTotComps;
    private javax.swing.JLabel jLabelTotCompsFiles;
    private javax.swing.JList jListChargeError;
    private javax.swing.JList jListComps;
    private javax.swing.JList jListHConflict;
    private javax.swing.JList jListNameWoutNumb;
    private javax.swing.JList jListNumbWoutName;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanelCheckBox;
    private javax.swing.JPanel jPanelErrs;
    private javax.swing.JPanel jPanelTot;
    private javax.swing.JScrollPane jScrollChargeError;
    private javax.swing.JScrollPane jScrollHConflict;
    private javax.swing.JScrollPane jScrollNameWoutNumb;
    private javax.swing.JScrollPane jScrollNumbWoutName;
    private javax.swing.JScrollPane jScrollPaneComps;
    private javax.swing.JSeparator jSeparator1;
    // End of variables declaration//GEN-END:variables
}
